/**
 * @file wrapper.hpp
 *
 * Contains the Wrapper class and extra defines to use.
 **/
#ifndef YEPXML_WRAPPER_HPP
#define YEPXML_WRAPPER_HPP

#include "node.hpp"

namespace yxml
{
    /**
     * @brief Automatizes the convertion between objects and xml elements.
     *
     *  A set of
     **/
    class Wrapper
    {
    public:
        /**
         * @brief Constructor.
         **/
        Wrapper( const string& tagName ): _tagName( tagName ) {}

        /**
         * @brief Recover a xml tag node into a object.
         **/
        virtual void recover( Node& tagNode, void* object ) = 0;

        /**
         * @brief Translate a object into a xml tag node.
         **/
        virtual void translate( const void* object, Node& tagNode ) = 0;

        /**
         * @brief Gets the tag name.
         **/
        const string& tagName()
        {
            return _tagName;
        }

        /**
         * @brief Adds a wrapper.
         **/
        static void insertWrapper( Wrapper& wrapper )
        {
            _wrappers[wrapper.tagName()] = &wrapper;
        }

        /**
         * @brief Removes a wrapper.
         **/
        static void eraseWrapper( const string& tagName )
        {
            _wrappers.erase( tagName );
        }

        /**
         * @brief get a wrapper.
         **/
        static Wrapper& wrapper( const string& tagName )
        {
            return *_wrappers[tagName];
        }

    private:
        string _tagName;
        static map<string, Wrapper*> _wrappers;
    };
}

/**@def YEPXML_BeginWrapper
 * @brief Declares the wrapper for the given class
 **/
#define YEPXML_BeginWrapper(className) class className##Wrapper: public yxml::Wrapper{\
    public: \
        className##Wrapper(): Wrapper(#className){}

/**@def YEPXML_EndWrapper
 * @brief Ends the wrapper declaration for the given class
 **/
#define YEPXML_EndWrapper(className) }; static className##Wrapper wrapper;

/**@def YEPXML_BeginRecover
 * @brief Initializes the recover definitions
 **/
#define YEPXML_BeginRecover(className) void recover(Node &tagNode, void *rawObject){\
        className &object = *static_cast<className*>( rawObject );

/**@def YEPXML_EndRecover
 * @brief Ends the recover definition
 **/
#define YEPXML_EndRecover() }

/**@def YEPXML_BeginAttributesRecover
 * @brief Initializes the attributes recover definitions
 **/
#define YEPXML_BeginAttributesRecover() for( map<string, string>::const_iterator it = tagNode.attributeBegin(); \
        it != tagNode.attributeEnd(); ++it ){
/**@def YEPXML_EndAttributesRecover
 * @brief Ends the attributes recover definition
 **/
#define YEPXML_EndAttributesRecover() ;}

/**@def YEPXML_RecoverAttribute
 * @brief Recover an attibute as a string
 **/
#define YEPXML_RecoverAttribute(key) if( it->first == #key ) object.key(it->second); else

/**@def YEPXML_RecoverTypeAttribute
 * @brief Recover an attibute as a numper of given type
 **/
#define YEPXML_RecoverTypeAttribute(key,type) if( it->first == #key ) {\
        type value;\
        stringstream( it->second ) >> value;\
        object.number( value );} else

#define YEPXML_BeginChildrenRecover() Node *child = &tagNode.firstChild();\
    while(child->type() != NONE)\
    {\
        switch(child->type())\
        {
#define YEPXML_EndChildrenRecover() default:\
    break;\
    }\
    child = &child->nextBrother();\
            }

#define YEPXML_BeginTagRecover() case TAG:

#define YEPXML_EndTagRecover() ;break;

#define YEPXML_RecoverTag(childClassName,insertMethod)if(child->value()==#childClassName)\
    {\
        childClassName childObject;\
        childClassName::wrapper.recover(*child, &childObject);\
        object.insertMethod(childObject);\
    }else

/**@def YEPXML_BeginTranslate
 * @brief Initializes the recover definitions
 **/
#define YEPXML_BeginTranslate(className) void translate( const void *rawObject,Node &tagNode){\
        const className &object = *static_cast<const className*>( rawObject );\
        tagNode.value(tagName());

/**@def YEPXML_EndTranslate
 * @brief Ends the recover definition
 **/
#define YEPXML_EndTranslate() }

/**@def YEPXML_TranslateAttribute
 * @brief Translate an attibute as a string
 **/
#define YEPXML_TranslateAttribute(key) tagNode.attribute(#key) = object.key();

/**@def YEPXML_TranslateTypeAttribute
 * @brief Translate an attibute as a number.
 **/
#define YEPXML_TranslateTypeAttribute(key) tagNode.attribute(#key) = dynamic_cast<stringstream&>(stringstream()<< object.key()).str();

/**@def YEPXML_TranslateTagUnique
 * @brief Translate a tag witch class is an attribute from the current translated class.
 **/
#define YEPXML_TranslateTagUnique(childClassName,getter) childClassName::wrapper.translate(&object.getter, tagNode.addChild(TAG, #childClassName));

#define YEPXML_TranslateTags(childClassName,begin,end) for(auto it=object.begin; it != object.end; ++it)\
        childClassName::wrapper.translate(&(*it), tagNode.addChild(TAG, #childClassName));

/**@def YEPXML_ImplementWrapperStatic
 * @brief Creates the static wrapper member implementation for the given class
 **/
#define YEPXML_ImplementWrapperStatic(className) className::className##Wrapper className::wrapper;

/**@def YEPXML_DeclareWrapper
 * @brief Declares a wrapper;
 **/
#define YEPXML_DeclareWrapper(className) Wrapper::insertWrapper(className::wrapper)

/**
 * @brief YEPXML_DeclareSave
 * @brief Declares a save function that writes on output stream. It is optional, but useful.
 **/
#define YEPXML_DeclareSave() void save(ostream &out)\
    {\
        Document doc("");\
        wrapper.translate(this, doc);\
        HierarchicalWritter writter(out);\
        writter.write(doc);\
    }

/**@def YEPXML_DeclareConstructor
 * @brief Declares a utility constructor, that reads from a input stream. It is optional, but useful
 **/
#define YEPXML_DeclareConstructor(className) className( istream& source )\
    {\
        Document doc("");\
        Reader reader(source);\
        reader.read(doc);\
        wrapper.recover(doc, this);\
    }

#endif // YEPXML_WRAPPER_HPP

